package com.zeng.sys.authmd.restfulmodule.interceptor;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.zeng.nicecommon.util.ResponseUtil;
import com.zeng.sys.authmd.apimodule.service.UserService;
import com.zeng.sys.authmd.apimodule.vo.UserVo;
import com.zeng.sys.authmd.restfulmodule.util.TokenUtil;
import org.aspectj.apache.bcel.classfile.Code;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.lang.ref.ReferenceQueue;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class AuthInterceptor implements HandlerInterceptor {

    private static Set<Object> exclude;

    private static Map<String, Set<String>> userApi = new ConcurrentHashMap<>();

    private Logger logger = LoggerFactory.getLogger(AuthInterceptor.class);

    @Autowired
    UserService userService;

    public AuthInterceptor() {
    }

    public static Set<Object> getExclude() {
        return exclude;
    }

    public static void setExclude(Set<Object> exclude) {
        AuthInterceptor.exclude = exclude;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
        String token = request.getHeader("token");
        logger.debug("外部访问地址:[{}]", request.getRequestURI());

        //配置公共资源部需要验证
        if (Optional.ofNullable(exclude).orElse(new HashSet<>()).contains(request.getRequestURI())) {
            return true;
        }

        if (token == null) {
            request.setAttribute("res", ResponseUtil.noAuthority("请登录"));
            request.getRequestDispatcher("/sys/failReq").forward(request, response);
            return false;
        }

        //查看token是否是本服务器产生
        if (!TokenUtil.isToken(token)) {
            request.setAttribute("res", ResponseUtil.noAuthority("请登录"));
            request.getRequestDispatcher("/sys/failReq").forward(request, response);
            return false;
        }

        // 获取token的公共信息
        String code = null;
        Date expiresAt = null;
        try {
            DecodedJWT decode = JWT.decode(token);
            code = decode.getAudience().get(0);
            expiresAt = decode.getExpiresAt();
        } catch (Exception e) {
            request.setAttribute("res", ResponseUtil.noAuthority("请重新登录"));
            request.getRequestDispatcher("/sys/failReq").forward(request, response);
            return false;
        }
        if (expiresAt.getTime() < System.currentTimeMillis()) {
            // 移除超时token
            TokenUtil.removeToken(token);
            request.setAttribute("res", ResponseUtil.noAuthority("登录超时,请重新登录"));
            request.getRequestDispatcher("/sys/failReq").forward(request, response);
            return false;
        }

        //从缓存取用户信息
        UserVo user = TokenUtil.getLoginUser(code);
        if (user == null) {
            logger.debug("从缓存中没有获取到user");
            user = userService.getOneByCode(code);
            if (user == null) {
                request.setAttribute("res", ResponseUtil.noAuthority("身份验证不通过,请重新登录"));
                request.getRequestDispatcher("/sys/failReq").forward(request, response);
                return false;
            }
            //添加缓存
            TokenUtil.addLoginUser(code, user);
        }

        request.setAttribute("user", Optional.ofNullable(user));
        logger.debug("成功将user添加入request:[{}]", user);

        // 校验token
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getUserPw())).build();

        try {
            jwtVerifier.verify(token);
        } catch (JWTVerificationException e) {
            request.setAttribute("res", ResponseUtil.noAuthority("无效签名，请重新登录"));
            request.getRequestDispatcher("/sys/failReq").forward(request, response);
            return false;
        }
        if (!apiVertify(user, request.getRequestURI())) {
            request.setAttribute("res", ResponseUtil.noAuthority("你没有访问此接口的权限").addData("status", "api-401"));
            request.getRequestDispatcher("/sys/failReq").forward(request, response);
            return false;
        }

        return true;

    }

    private boolean apiVertify(UserVo userVo, String url) {
        if (userVo.getUserAccount().equals("admin")) {
            return true;
        }

        String userCode = userVo.getCode();
        if (userApi.get(userCode) == null) {
            UserVo userVoWithApis = userService.listUserApi(userCode);
            userVoWithApis.getApis().add("/menu/list");
            userApi.put(userCode, userVoWithApis.getApis());
        }
        Set<String> apis = userApi.get(userCode);
        if (apis.contains(url)) {
            return true;
        }
        return false;
    }

    public static void clearUserApiByCode(String userCode) {
        userApi.remove(userCode);
    }

    public static void clearAllUserApi() {
        userApi.clear();
    }

}
